/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.interpreter.backends.rdf;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import xowl.interpreter.frontends.VocRDF;

/**
 *
 * @author Laurent WOUTERS
 */
public abstract class RDFRepository extends xowl.interpreter.impl.BaseRepository {
    static final String name = "XOWL RDF";
    
    protected class ObserverEnabler implements RDFChangeListener {
        @Override public void onChange(xowl.interpreter.impl.AggregatedEvaluator evaluator, RDFChange change) {
            RDFIRIReference iriRef = (RDFIRIReference)change.getTriple().getProperty();
            xowl.lang.owl2.IRI iri = iriRef.getIRI();
            String strIRI = iri.getHasValue();
            if (VocRDF.rdfType.equals(strIRI)) {
                try {
                    xowl.lang.runtime.Value vClass = getValue(change.getTriple().getObject());
                    xowl.lang.runtime.Value vInd = getValue(change.getTriple().getSubject());
                    xowl.lang.runtime.Class _class = null;
                    if (vClass instanceof xowl.lang.runtime.Class)
                        _class = (xowl.lang.runtime.Class)vClass;
                    else if (vClass instanceof xowl.lang.runtime.Entity)
                        _class = interpretAsClass((xowl.lang.runtime.Entity)vClass);
                    xowl.lang.runtime.Individual _ind = null;
                    if (vInd instanceof xowl.lang.owl2.AnonymousIndividual)
                        _ind = (xowl.lang.owl2.AnonymousIndividual)vInd;
                    else if (vInd instanceof xowl.lang.runtime.Entity)
                        _ind = interpretAsIndividual((xowl.lang.runtime.Entity)vInd);
                    if (change.isAdd()) onClassInstantiate(_class, _ind);
                    else onClassDestroy(_class, _ind);
                } catch (xowl.interpreter.XOWLException ex) {}
            } else {
                xowl.lang.runtime.Entity entity = resolveEntity(iri);
                for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs()) {
                    if (interpretation instanceof xowl.lang.runtime.ObjectProperty) {
                        xowl.lang.runtime.ObjectProperty prop = (xowl.lang.runtime.ObjectProperty)interpretation;
                        try {
                            xowl.lang.runtime.Value vSubject = getValue(change.getTriple().getSubject());
                            xowl.lang.runtime.Value vObject = getValue(change.getTriple().getObject());
                            xowl.lang.runtime.Individual _sub = null;
                            if (vSubject instanceof xowl.lang.owl2.AnonymousIndividual)
                                _sub = (xowl.lang.owl2.AnonymousIndividual)vSubject;
                            else if (vSubject instanceof xowl.lang.runtime.Entity)
                                _sub = interpretAsIndividual((xowl.lang.runtime.Entity)vSubject);
                            xowl.lang.runtime.Individual _obj = null;
                            if (vObject instanceof xowl.lang.owl2.AnonymousIndividual)
                                _obj = (xowl.lang.owl2.AnonymousIndividual)vObject;
                            else if (vObject instanceof xowl.lang.runtime.Entity)
                                _obj = interpretAsIndividual((xowl.lang.runtime.Entity)vObject);
                            if (change.isAdd()) onPropertyAddValue(_sub, prop, _obj);
                            else onPropertyRemoveValue(_sub, prop, _obj);
                        } catch (xowl.interpreter.XOWLException ex) {}
                    } else if (interpretation instanceof xowl.lang.runtime.DataProperty) {
                        xowl.lang.runtime.DataProperty prop = (xowl.lang.runtime.DataProperty)interpretation;
                        try {
                            xowl.lang.runtime.Value vSubject = getValue(change.getTriple().getSubject());
                            xowl.lang.runtime.Value vObject = getValue(change.getTriple().getObject());
                            xowl.lang.runtime.Individual _sub = null;
                            if (vSubject instanceof xowl.lang.owl2.AnonymousIndividual)
                                _sub = (xowl.lang.owl2.AnonymousIndividual)vSubject;
                            else if (vSubject instanceof xowl.lang.runtime.Entity)
                                _sub = interpretAsIndividual((xowl.lang.runtime.Entity)vSubject);
                            xowl.lang.runtime.Literal _obj = null;
                            if (vObject instanceof xowl.lang.runtime.Literal)
                                _obj = (xowl.lang.runtime.Literal)vObject;
                            if (change.isAdd()) onPropertyAddValue(_sub, prop, _obj);
                            else onPropertyRemoveValue(_sub, prop, _obj);
                        } catch (xowl.interpreter.XOWLException ex) {}
                    }
                }
            }
        }
        @Override public void onChanges(xowl.interpreter.impl.AggregatedEvaluator evaluator, RDFChangeset changeset) {
            for (RDFChange change : changeset.getRDFChanges())
                onChange(evaluator, change);
        }
    }
    
    protected RDFGraph rdfGraph;
    protected TranslatorXOWL_RDF xOWLTranslator;
    protected TranslatorRDF_OWL2 rdfTranslator;
    protected RDFRuleEngine ruleEngine;
    protected ObserverEnabler observer;

    public RDFGraph getGraph() { return rdfGraph; }
    public TranslatorXOWL_RDF getXOWLTranslator() { return xOWLTranslator; }

    public RDFRepository(xowl.utils.logging.Logger log) {
        super(log);
        xowl.interpreter.impl.Evaluator staticEvaluator = new xowl.interpreter.impl.DefaultStaticEvaluator(this);
        evaluatorParts.add(staticEvaluator);
        defaultEvaluator.addPart(staticEvaluator);
        rdfGraph = new RDFGraph();
        xOWLTranslator = new TranslatorXOWL_RDF(this, rdfGraph);
        rdfTranslator = new TranslatorRDF_OWL2(this);
        ruleEngine = new RDFRuleEngine(this, rdfGraph);
        observer = new ObserverEnabler();
        rdfGraph.addEventHandler(ruleEngine, 0);
        rdfGraph.addEventHandler(observer, 10);
    }
    
    public xowl.lang.runtime.Value getValue(RDFNode node) throws xowl.interpreter.XOWLException {
        if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.IRIReference) {
            xowl.lang.owl2.IRI iri = ((xowl.interpreter.backends.rdf.RDFIRIReference)node).getIRI();
            return resolveEntity(iri);
        } else if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Blank) {
            // Shit happened !
            throw new xowl.interpreter.XOWLException("Cannot translate a  blank node!");
        } else if (node.getNodeType() == xowl.interpreter.backends.rdf.RDFNodeType.Anonymous)
            return ((xowl.interpreter.backends.rdf.RDFBlankNode)node).getOWLIndividual();
        return ((xowl.interpreter.backends.rdf.RDFLiteralNode)node).getLiteral();
    }
    public RDFNode getRDFNode(xowl.lang.runtime.Value value) throws xowl.interpreter.XOWLException {
        if (value instanceof xowl.lang.runtime.Entity) {
            return rdfGraph.getEntityNode(((xowl.lang.runtime.Entity)value).getHasIRI());
        } else if (value instanceof xowl.lang.runtime.Literal) {
            return rdfGraph.getLiteralNode((xowl.lang.runtime.Literal)value);
        } else if (value instanceof xowl.lang.owl2.AnonymousIndividual) {
            return rdfGraph.getBlankNode((xowl.lang.owl2.AnonymousIndividual)value);
        }
        throw new xowl.interpreter.XOWLException("Cannot translate " + value.getClass().getCanonicalName());
    }

    @Override public void dropOntology(xowl.lang.owl2.Ontology ontology) {
        rdfGraph.removeOntology(ontology);
    }
    
    @Override public Collection<xowl.lang.owl2.Axiom> getAxioms(xowl.lang.owl2.Ontology ontology) {
        return rdfTranslator.translateOntology(defaultEvaluator, rdfGraph, ontology);
    }
    
    @Override public void apply(xowl.interpreter.impl.AggregatedEvaluator evaluator, xowl.interpreter.Change change) throws xowl.interpreter.XOWLException {
        xowl.lang.owl2.Axiom axiom = change.getAxiom();
        java.lang.Class c = axiom.getClass();
        if (c == xowl.lang.actions.FunctionDefinition.class)
            applyAxiomFunctionDefinition(evaluator, (xowl.lang.actions.FunctionDefinition)axiom, change.isAdd());
        else {
            List<RDFChange> buffer = translateChange(evaluator, change);
            RDFChangeset changeset = new RDFChangeset();
            for (RDFChange rdf : buffer)
                changeset.add(rdf);
            apply(evaluator, changeset);
        }
    }
    @Override public void apply(xowl.interpreter.impl.AggregatedEvaluator evaluator, xowl.interpreter.Changeset changeset) throws xowl.interpreter.XOWLException {
        if (changeset instanceof RDFChangeset) {
            RDFChangeset rdf = (RDFChangeset)changeset;
            apply(evaluator, rdf);
        } else {
            RDFChangeset rdfChangeset = new RDFChangeset();
            for (xowl.interpreter.Change change : changeset.getChanges()) {
                xowl.lang.owl2.Axiom axiom = change.getAxiom();
                java.lang.Class c = axiom.getClass();
                if (c == xowl.lang.actions.FunctionDefinition.class)
                    applyAxiomFunctionDefinition(evaluator, (xowl.lang.actions.FunctionDefinition)axiom, change.isAdd());
                else
                    for (RDFChange rdf : translateChange(evaluator, change))
                        rdfChangeset.add(rdf);
            }
            apply(evaluator, rdfChangeset);
        }
    }
    protected void apply(xowl.interpreter.impl.AggregatedEvaluator evaluator, RDFChangeset changeset) {
        for (RDFChange change : changeset.getRDFChanges())
            addTransactionElement(new RDFTransactionElement(this, change.getOntology(), change.getTriple(), change.isAdd()));
        rdfGraph.onChanges(evaluator, changeset);
    }
    
    protected List<RDFChange> translateChange(xowl.interpreter.impl.Evaluator evaluator, xowl.interpreter.Change change) throws xowl.interpreter.XOWLException {
        List<RDFChange> changes = new ArrayList<RDFChange>();
        xowl.interpreter.backends.rdf.Translation translation = xOWLTranslator.translateAxiom(evaluator, null, true, true, change.getAxiom());
            for (xowl.interpreter.backends.rdf.RDFTriple triple : translation.getTriples())
                changes.add(new RDFChange(triple, change.getOntology(), change.isAdd()));
        return changes;
    }
    
    protected void undo(RDFTransactionElement element) { rdfGraph.onChange(defaultEvaluator, element.getNegative()); }
    protected void redo(RDFTransactionElement element) { rdfGraph.onChange(defaultEvaluator, element); }
    
    @Override public void apply(xowl.interpreter.impl.AggregatedEvaluator evaluator, xowl.interpreter.RuleSet ruleset) { this.ruleEngine.addRules(evaluator, null, null, null, ruleset); }
    @Override public void unapply(xowl.interpreter.impl.AggregatedEvaluator evaluator, xowl.interpreter.RuleSet ruleset) { this.ruleEngine.removeRules(ruleset); }
    
    @Override public void apply(xowl.interpreter.impl.AggregatedEvaluator evaluator, xowl.interpreter.Transformation transformation) { this.ruleEngine.addRules(evaluator, transformation.getInputOntology(), transformation.getTargetOntology(), transformation.getMetaOntology(), transformation.getRuleset()); }
    @Override public void unapply(xowl.interpreter.impl.AggregatedEvaluator evaluator, xowl.interpreter.Transformation transformation) { this.ruleEngine.removeRules(transformation.getRuleset()); }
    
    
    @Override public xowl.interpreter.Transformer createTransformer(xowl.interpreter.Transformation transformation, boolean bindMeta) {
        return new RDFTransformer(defaultEvaluator, this, transformation, bindMeta);
    }
}
